package com.xxx.common.excel;

import com.xxx.common.enums.base.BaseEnum;
import com.xxx.common.excel.annotation.ExcelField;
import com.xxx.common.excel.annotation.ExcelSheet;
import com.xxx.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * Excel导出工具
 *
 * @author xuxueli 2017-09-08 22:27:20
 */
@Slf4j
public class ExcelExportUtil {


    /**
     * 导出Excel对象
     *
     * @param dataList Excel数据
     * @return
     */
    public static Workbook exportWorkbook(List<?> dataList) {

        // data
        if (dataList == null || dataList.size() == 0) {
            throw new RuntimeException(">>>>>>>>>>> xxl-excel error, data can not be empty.");
        }

        // sheet
        Class<?> sheetClass = dataList.get(0).getClass();
        ExcelSheet excelSheet = sheetClass.getAnnotation(ExcelSheet.class);

        String sheetName = dataList.get(0).getClass().getSimpleName();
        if (excelSheet != null) {
            if (excelSheet.name() != null && excelSheet.name().trim().length() > 0) {
                sheetName = excelSheet.name().trim();
            }
        }


        // sheet field
        List<Field> fields = new ArrayList<Field>();
        if (sheetClass.getDeclaredFields() != null && sheetClass.getDeclaredFields().length > 0) {
            for (Field field : sheetClass.getDeclaredFields()) {
                if (Modifier.isStatic(field.getModifiers())) {
                    continue;
                }
                fields.add(field);
            }
        }

        if (fields == null || fields.size() == 0) {
            throw new RuntimeException(">>>>>>>>>>> xxl-excel error, data field can not be empty.");
        }

        // book
        // HSSFWorkbook=2003/xls、XSSFWorkbook=2007/xlsx
        Workbook workbook = new HSSFWorkbook();
        Sheet sheet = workbook.createSheet(sheetName);


        sheet.setDefaultColumnWidth(20);
        sheet.setDefaultRowHeightInPoints(20);

        // sheet header row
        CellStyle headStyle = workbook.createCellStyle();

        Row headRow = sheet.createRow(0);
        int cellIndex = 0;
        for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);
            ExcelField excelField = field.getAnnotation(ExcelField.class);
            String fieldName = (excelField != null && excelField.name() != null && excelField.name().trim().length() > 0) ? excelField.name() : field.getName();

            Cell cellX = headRow.createCell(excelField.orderNum() != -1 ? excelField.orderNum() : cellIndex, CellType.STRING);
            if (headStyle != null) {
                cellX.setCellStyle(headStyle);
            }
            cellX.setCellValue(String.valueOf(fieldName));
            sheet.setColumnWidth(cellX.getColumnIndex(), excelField.width() * 256);
            sheet.setColumnWidth(cellX.getColumnIndex(), excelField.height() * 256);
            cellIndex++;
        }

        // sheet data rows
        for (int dataIndex = 0; dataIndex < dataList.size(); dataIndex++) {
            int rowIndex = dataIndex + 1;
            Object rowData = dataList.get(dataIndex);

            Row rowX = sheet.createRow(rowIndex);
            cellIndex = 0;
            for (int i = 0; i < fields.size(); i++) {
                Field field = fields.get(i);
                ExcelField excelField = field.getAnnotation(ExcelField.class);
                try {
                    field.setAccessible(true);
                    Object fieldValue = field.get(rowData) == null ? "" : field.get(rowData);
                    Cell cellX = rowX.createCell(excelField.orderNum() != -1 ? excelField.orderNum() : cellIndex, CellType.STRING);
                    cellX.setCellValue(String.valueOf(fieldValue));
                    cellIndex++;
                } catch (IllegalAccessException e) {
                    if(log.isErrorEnabled()) {
                        log.error(e.getMessage(), e);
                    }
                    throw new RuntimeException(e);
                }
            }
        }

        return workbook;
    }

    /**
     * 导出Excel文件到磁盘
     *
     * @param dataList
     * @param filePath
     */
    public static void exportToFile(List<?> dataList, String filePath) {
        // workbook
        Workbook workbook = exportWorkbook(dataList);

        FileOutputStream fileOutputStream = null;
        try {
            // workbook 2 FileOutputStream
            fileOutputStream = new FileOutputStream(filePath);
            workbook.write(fileOutputStream);

            // flush
            fileOutputStream.flush();
        } catch (Exception e) {
            if(log.isErrorEnabled()) {
                log.error(e.getMessage(), e);
            }
            throw new RuntimeException(e);
        } finally {
            closeStream(fileOutputStream);
        }
    }

    /**
     * 导出Excel字节数据
     *
     * @param dataList
     * @return
     */
    public static byte[] exportToBytes(List<?> dataList) {
        // workbook
        Workbook workbook = exportWorkbook(dataList);

        ByteArrayOutputStream byteArrayOutputStream = null;
        byte[] result = null;
        try {
            // workbook 2 ByteArrayOutputStream
            byteArrayOutputStream = new ByteArrayOutputStream();
            workbook.write(byteArrayOutputStream);

            // flush
            byteArrayOutputStream.flush();

            result = byteArrayOutputStream.toByteArray();
            return result;
        } catch (Exception e) {
            if(log.isErrorEnabled()) {
                log.error(e.getMessage(), e);
            }
            throw new RuntimeException(e);
        } finally {
            closeStream(byteArrayOutputStream);
        }
    }

    private static void closeStream(OutputStream outputStream) {
        try {
            if (outputStream != null) {
                outputStream.close();
            }
        } catch (Exception e) {
            if(log.isErrorEnabled()) {
                log.error(e.getMessage(), e);
            }
            throw new RuntimeException(e);
        }
    }


    /**
     * CSV文件生成方法
     *
     * @param dataList
     * @param dataList
     * @return
     */
    public static byte[]  exportCSVToBytes(List<?> dataList) {


        // data
        if (dataList == null || dataList.size() == 0) {
            throw new RuntimeException(">>>>>>>>>>> xxl-excel error, data can not be empty.");
        }

        // sheet
        Class<?> sheetClass = dataList.get(0).getClass();
        ExcelSheet excelSheet = sheetClass.getAnnotation(ExcelSheet.class);

        // sheet field
        List<Field> fields = new ArrayList<Field>();
        if (sheetClass.getDeclaredFields() != null && sheetClass.getDeclaredFields().length > 0) {
            for (Field field : sheetClass.getDeclaredFields()) {
                if (Modifier.isStatic(field.getModifiers())) {
                    continue;
                }
                ExcelField excelField = field.getAnnotation(ExcelField.class);
                if (excelField == null) {
                    continue;
                }
                fields.add(field);
            }
        }

        if (fields == null || fields.size() == 0) {
            throw new RuntimeException(">>>>>>>>>>> xxl-excel error, data field can not be empty.");
        }

        fields = fieldOrder(fields);

        BufferedWriter csvWtriter = null;
        try {

            ByteArrayOutputStream byteArrayOutputStream = null;
            byte[] result = null;
            // workbook 2 ByteArrayOutputStream
            byteArrayOutputStream = new ByteArrayOutputStream();

            // GB2312使正确读取分隔符","
            csvWtriter = new BufferedWriter(new OutputStreamWriter(byteArrayOutputStream, "UTF-8"), 1024);

            // 写入文件头部
            writeRowHead(fields, csvWtriter);


            // 写入文件内容
            for (int dataIndex = 0; dataIndex < dataList.size(); dataIndex++) {
                Object rowData = dataList.get(dataIndex);
                writeRow(fields, rowData, csvWtriter);
            }
            csvWtriter.flush();
            return byteArrayOutputStream.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            try {
                csvWtriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 写一行数据方法
     * @param csvWriter
     * @throws IOException
     */
    private static void writeRow(List<Field> fields,Object obj, BufferedWriter csvWriter) {
        try {
            // 写入文件头部
            for (Field data : fields) {
                StringBuffer sb = new StringBuffer();
                data.setAccessible(true);
                Object fieldValue = "";

                if(java.util.Date.class.equals(data.getType()) && data.get(obj) != null){
                    fieldValue = DateUtil.formatDate((Date) data.get(obj),DateUtil.defaultFormat);
                } else if(data.get(obj) != null && data.get(obj) instanceof  BaseEnum){
                    fieldValue = ((BaseEnum)data.get(obj)).getDescription();
                } else {
                    fieldValue = data.get(obj) == null ? "" : data.get(obj);
                }
                String rowStr = sb.append("\"").append(String.valueOf(fieldValue)).append("\",").toString();
                csvWriter.write(rowStr);
            }
            csvWriter.newLine();
        } catch (IllegalAccessException e){
            if(log.isErrorEnabled()) {
                log.error(e.getMessage(), e);
            }
                throw new RuntimeException(e);
        }catch (Exception e){
            if(log.isErrorEnabled()) {
                log.error(e.getMessage(), e);
            }
            throw new RuntimeException(e);
        }

    }


    /**
     * 写头方法
     * @param csvWriter
     * @throws IOException
     */
    private static void writeRowHead(List<Field> fields, BufferedWriter csvWriter) {
        try {
            // 写入文件头部
            for (Field data : fields) {
                StringBuffer sb = new StringBuffer();
                ExcelField excelField1 = data.getAnnotation(ExcelField.class);
                String rowStr = sb.append("\"").append(excelField1.name()).append("\",").toString();
                csvWriter.write(rowStr);
            }
            csvWriter.newLine();
        }catch (Exception e){
            if(log.isErrorEnabled()) {
                log.error(e.getMessage(), e);
            }
            throw new RuntimeException(e);
        }
    }


    private static List<Field> fieldOrder(List<Field> fields){
        Collections.sort(fields, new Comparator<Field>() {
            @Override
            public int compare(Field o1, Field o2) {

                ExcelField excelField1 = o1.getAnnotation(ExcelField.class);

                ExcelField excelField2 = o2.getAnnotation(ExcelField.class);

                int i = excelField1.orderNum() - excelField2.orderNum();
                if(i == 0){
                    return excelField1.orderNum();
                }
                return i;
            }
        });
        return fields;
    }

}
